use std::{ffi::OsString, path::PathBuf};

use im_rc::{HashMap, Vector};

use crate::{FUNC, ZERO_C};

#[derive(Debug, PartialEq, Eq)]
enum Token {
    Symbol(String),
    String(String),
    Integer(i64),
    LoadVar(String), // $
    EndStat,         // ;
    Do,
    End,
    Else,
    Elif,
    CallBegin, // (
    CallEnd,   // )
    EndCode,
}

pub struct Source {
    code: Vec<char>,
    curr: usize,
    curr_c: char,
    row: usize,
    col: usize,
}

impl Source {
    #[cfg(test)]
    fn from_string(s: String) -> Self {
        Self::new(s.chars().collect())
    }

    fn load_token(&mut self) -> Result<Token, ()> {
        self.skip_ws();

        Ok(match self.peek() {
            ZERO_C => Token::EndCode,
            ';' => {
                self.pop();
                Token::EndStat
            }
            '(' => {
                self.pop();
                Token::CallBegin
            }
            ')' => {
                self.pop();
                Token::CallEnd
            }
            '#' => loop {
                if self.peek() == ZERO_C || self.peek() == '\n' {
                    return self.load_token();
                }
                self.pop();
            },
            '$' => {
                self.pop();
                Token::LoadVar(self.load_symbol())
            }
            '"' => self.load_string()?,
            '=' | '-' | '/' | '~' | '*' | '.' | '+' | 'a'..='z' | 'A'..='Z' => {
                let s = self.load_symbol();

                match s.as_str() {
                    "do" => Token::Do,
                    "end" => Token::End,
                    "else" => Token::Else,
                    "elif" => Token::Elif,
                    _ => Token::Symbol(s),
                }
            }
            '0'..='9' => {
                let mut n = String::new();

                while let '0'..='9' = self.peek() {
                    n.push(self.peek());
                    self.pop();
                }

                let n: i64 = n
                    .parse()
                    .map_err(|err| eprintln!("failed to parse: {n} as interger: {err}"))?;

                Token::Integer(n)
            }
            _ => {
                eprintln!("invalid leading char: {:?}", self.peek());
                return Err(());
            }
        })
    }

    fn load_string(&mut self) -> Result<Token, ()> {
        self.pop();

        let mut s = String::new();
        let mut escape = false;

        loop {
            if self.peek() == ZERO_C {
                eprintln!("reach source code end before end of string");
                return Err(());
            }

            if escape {
                let c = match self.peek() {
                    'n' => '\n',
                    't' => '\t',
                    'r' => '\r',
                    '0' => '\0',
                    '"' => '"',
                    '\\' => '\\',
                    c => {
                        eprintln!("invalid escape of {c:?}");
                        return Err(());
                    }
                };

                escape = false;
                s.push(c);
            } else {
                match self.peek() {
                    '\\' => escape = true,
                    '"' => {
                        self.pop();
                        break;
                    }
                    c => {
                        s.push(c);
                    }
                }
            }

            self.pop();
        }

        Ok(Token::String(s))
    }

    fn load_symbol(&mut self) -> String {
        let mut s = String::new();

        s.push(self.peek());
        self.pop();

        while let '=' | '-' | '/' | '~' | '*' | '.' | '_' | 'a'..='z' | 'A'..='Z' | '0'..='9' =
            self.peek()
        {
            s.push(self.peek());
            self.pop();
        }

        s
    }

    fn skip_ws(&mut self) {
        loop {
            match self.peek() {
                ' ' | '\t' | '\r' | '\n' => {
                    self.pop();
                }
                _ => return,
            }
        }
    }

    pub fn new(code: Vec<char>) -> Self {
        let curr_c = if code.is_empty() { 0 as char } else { code[0] };

        Self {
            code,
            curr: 0,
            curr_c,
            row: 1,
            col: 1,
        }
    }

    fn peek(&self) -> char {
        self.curr_c
    }

    fn pop(&mut self) {
        if self.curr < self.code.len() {
            self.curr += 1;
        }

        if self.curr_c == '\n' {
            self.row += 1;
            self.col = 1;
        } else {
            self.col += 1;
        }

        self.curr_c = self.code.get(self.curr).copied().unwrap_or(0 as char);
    }
}

#[derive(Debug)]
pub enum Block {
    Func(Func),
    End,
}

#[derive(Debug)]
pub struct Func {
    pub name: String,
    pub stats: Vec<Stat>,
}

#[derive(Debug)]
pub enum Stat {
    // Control flows.
    Continue,
    Break,
    Return {
        expr: Expr,
    },

    FuncCall {
        name: String,
        args: Vec<Expr>,
    },
    Let {
        name: String,
        expr: Expr,
    },
    CdBlock {
        path: Expr,
        stats: Vec<Stat>,
    },

    Foreach {
        iterator: String,
        iterable: Expr,
        body: Vec<Stat>,
    },
    While {
        cond: Expr,
        body: Vec<Stat>,
    },

    If {
        branches: Vec<IfBranch>,
        otherwise: Vec<Stat>,
    },
}

#[derive(Debug)]
pub struct IfBranch {
    pub cond: Expr,
    pub then: Vec<Stat>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Value {
    Bool(bool),
    Symbol(String),
    Path(PathBuf),
    OsString(OsString),
    String(String),
    Integer(i64),
    Array(Vector<Value>),
    Hash(HashMap<String, Value>),
}

impl Value {
    pub fn as_integer(&self) -> Result<i64, ()> {
        Ok(match self {
            Value::Integer(x) => *x,
            _ => {
                eprintln!("expect integer, found: {self:?}");
                return Err(());
            }
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expr {
    Value(Value),
    LoadVar { name: String },
    FuncCall { name: String, args: Vec<Expr> },
    And { conds: Vec<Expr> },
    Or { conds: Vec<Expr> },
}

pub struct Parser {
    source: Source,
    token: Token,
}

impl Parser {
    pub fn new(mut source: Source) -> Result<Self, ()> {
        let token = source.load_token()?;
        Ok(Self { source, token })
    }

    fn peek(&self) -> &Token {
        &self.token
    }

    fn pop(&mut self) -> Result<Token, ()> {
        if let Token::EndCode = self.token {
            return Ok(Token::EndCode);
        }

        Ok(std::mem::replace(
            &mut self.token,
            self.source.load_token()?,
        ))
    }

    fn parse_func(&mut self) -> Result<Func, ()> {
        self.pop()?; // func

        let Token::Symbol(name) = self.pop()? else {
            eprintln!("missing function name");
            return Err(());
        };

        let mut stats = vec![];

        loop {
            if self.peek() == &Token::End {
                self.pop()?;
                break;
            }

            stats.push(self.parse_stat()?);
        }

        Ok(Func { name, stats })
    }

    fn parse_stat(&mut self) -> Result<Stat, ()> {
        let head = match self.pop() {
            Ok(Token::Symbol(head)) => head,
            token => {
                eprintln!("invalid leading token of statement: {token:?}");
                return Err(());
            }
        };

        match head.as_str() {
            "if" => self.parse_if(),
            "for" => self.parse_foreach(),
            "while" => self.parse_while(),

            "return" => self.parse_return(),
            "break" => {
                self.expect_end_stat()?;
                Ok(Stat::Break)
            }
            "continue" => {
                self.expect_end_stat()?;
                Ok(Stat::Continue)
            }
            "let" => self
                .parse_let()
                .map_err(|_| eprintln!("failed to parse a set statement")),
            "cd" => self
                .parse_cd_block()
                .map_err(|_| eprintln!("failed to parse a cd-block")),
            _ => self
                .parse_func_call(head)
                .map_err(|_| eprintln!("failed to parse a function call")),
        }
    }

    fn parse_func_call(&mut self, head: String) -> Result<Stat, ()> {
        let mut args = vec![];

        loop {
            match self.peek() {
                Token::EndCode => {
                    eprintln!("reach source code end before end of function call");
                    return Err(());
                }
                Token::EndStat => {
                    self.pop()?;
                    break;
                }
                _ => {
                    args.push(self.parse_expr()?);
                }
            }
        }

        Ok(Stat::FuncCall { name: head, args })
    }

    fn parse_let(&mut self) -> Result<Stat, ()> {
        let name = match self.pop()? {
            Token::Symbol(symbol) => symbol,
            token => {
                eprintln!("let statement need a symbol instead of: {token:?}");
                return Err(());
            }
        };

        let expr = self.parse_expr()?;
        self.expect_end_stat()?;

        Ok(Stat::Let { name, expr })
    }

    fn parse_cd_block(&mut self) -> Result<Stat, ()> {
        let path = self.parse_expr()?;
        let stats = self.parse_code_block()?;
        Ok(Stat::CdBlock { path, stats })
    }

    fn parse_if_block(&mut self) -> Result<(Token, Vec<Stat>), ()> {
        let mut stats = vec![];
        let end_token;

        loop {
            match self.peek() {
                Token::EndCode => {
                    eprintln!("reach source code end before end of if block");
                    return Err(());
                }
                Token::End => {
                    self.pop()?;
                    end_token = Token::End;
                    break;
                }
                Token::Else => {
                    self.pop()?;
                    end_token = Token::Else;
                    break;
                }
                Token::Elif => {
                    self.pop()?;
                    end_token = Token::Elif;
                    break;
                }
                _ => (),
            };

            stats.push(self.parse_stat()?);
        }

        Ok((end_token, stats))
    }

    // xxxx
    //   stat
    //   stat
    // end
    fn parse_code_block(&mut self) -> Result<Vec<Stat>, ()> {
        let mut stats = vec![];

        loop {
            match self.peek() {
                Token::EndCode => {
                    eprintln!("reach source code end before end of block");
                    return Err(());
                }
                Token::End => {
                    self.pop()?;
                    break;
                }
                _ => (),
            };

            stats.push(self.parse_stat()?);
        }

        Ok(stats)
    }

    fn parse_expr(&mut self) -> Result<Expr, ()> {
        Ok(match self.pop()? {
            Token::Symbol(symbol) => Expr::Value(Value::Symbol(symbol)),
            Token::String(string) => Expr::Value(Value::String(string)),
            Token::Integer(n) => Expr::Value(Value::Integer(n)),
            Token::LoadVar(name) => Expr::LoadVar { name },

            Token::CallBegin => {
                let Token::Symbol(name) = self.pop()? else {
                    eprintln!("leading token of function call expression is not a symbol");
                    return Err(());
                };

                let mut args = vec![];

                loop {
                    if let Token::CallEnd = self.peek() {
                        self.pop()?;
                        break;
                    }

                    args.push(self.parse_expr()?);
                }

                match name.as_str() {
                    "and" => Expr::And { conds: args },
                    "or" => Expr::Or { conds: args },
                    _ => Expr::FuncCall { name, args },
                }
            }

            token => {
                eprintln!("invalid leading token of expression: {token:?}");
                return Err(());
            }
        })
    }

    fn parse_foreach(&mut self) -> Result<Stat, ()> {
        let iterator = match self.pop()? {
            Token::Symbol(symbol) => symbol,
            token => {
                eprintln!("for statement need a symbol instead of: {token:?}");
                return Err(());
            }
        };

        match self.pop()? {
            Token::Symbol(symbol) if symbol == "in" => symbol,
            token => {
                eprintln!("for statement need 'in' instead of: {token:?}");
                return Err(());
            }
        };

        let iterable = self.parse_expr()?;

        let body = self.parse_code_block()?;

        Ok(Stat::Foreach {
            iterator,
            iterable,
            body,
        })
    }

    fn parse_return(&mut self) -> Result<Stat, ()> {
        let expr = self.parse_expr()?;

        self.expect_end_stat()?;

        Ok(Stat::Return { expr })
    }

    fn expect_end_stat(&mut self) -> Result<(), ()> {
        let Token::EndStat = self.pop()? else {
            eprintln!("expect ';'");
            return Err(());
        };
        Ok(())
    }

    fn parse_if(&mut self) -> Result<Stat, ()> {
        let cond = self.parse_expr()?;
        let (mut branch_end_token, then) = self.parse_if_block()?;

        let mut branches = vec![IfBranch { cond, then }];
        let mut otherwise = vec![];

        loop {
            match &branch_end_token {
                Token::End => {
                    break;
                }
                Token::Else => {
                    otherwise = self.parse_code_block()?;
                    break;
                }
                Token::Elif => {
                    let cond = self.parse_expr()?;
                    let (new_end, then) = self.parse_if_block()?;
                    branches.push(IfBranch { cond, then });
                    branch_end_token = new_end;
                }
                _ => unreachable!(),
            }
        }

        Ok(Stat::If {
            branches,
            otherwise,
        })
    }

    fn parse_while(&mut self) -> Result<Stat, ()> {
        let cond = self.parse_expr()?;
        let body = self.parse_code_block()?;

        Ok(Stat::While { cond, body })
    }
}

#[test]
fn test_parse_expr() {
    let s = Source::from_string(r#"a "Hello" "#.to_string());
    let mut p = Parser::new(s).unwrap();

    assert_eq!(&Token::Symbol("a".to_string()), p.peek());
    p.pop().unwrap();

    assert_eq!(&Token::String("Hello".to_string()), p.peek());
    p.pop().unwrap();

    assert_eq!(&Token::EndCode, p.peek());
}

impl Parser {
    pub fn parse(&mut self) -> Result<Block, ()> {
        Ok(match self.peek() {
            Token::Symbol(symbol) => match symbol.as_str() {
                FUNC => Block::Func(self.parse_func()?),
                _ => {
                    eprintln!("invalid block starts with: {}", symbol);
                    return Err(());
                }
            },
            Token::EndCode => Block::End,
            _ => {
                eprintln!("invalid block starts with: {:?}", self.peek());
                return Err(());
            }
        })
    }
}
